import platform, itertools, os.path, inspect, time

import numpy

def check_nondecreasing(a, msg=""):
    for i in range(len(a) - 1):
        check_le(a[i], a[i + 1], msg=msg)

def check_increasing(a, msg=""):
    for i in range(len(a) - 1):
        check_lt(a[i], a[i + 1], msg=msg)

def check_nonincreasing(a, msg=""):
    for i in range(len(a) - 1):
        check_ge(a[i], a[i + 1], msg=msg)

def check_decreasing(a, msg=""):
    for i in range(len(a) - 1):
        check_gt(a[i], a[i + 1], msg=msg)

def check_is(x, y, msg=""):
    if x is not y:
        raise Exception("%s %r is not %r" % (msg, x, y))

def check_is_instance(x, y, msg=""):
    if not isinstance(x, y):
        raise Exception("%s %r is not an instance of %r" % (msg, x, y))

def check_equal(x, y, msg=""):
    if x != y:
        raise Exception("%s %r != %r" % (msg, x, y))

def check_not_equal(x, y, msg=""):
    if x == y:
        raise Exception("%s %r = %r" % (msg, x, y))

def check_True(x, msg=""):
    if not x:
        raise Exception("%s %r" % (msg, x))

def check_False(x, msg=""):
    if x:
        raise Exception("%s %r" % (msg, x))

def check_None(x, msg=""):
    if x is not None:
        raise Exception("%s %r is not None" % (msg, x))

def check_le(x, y, msg=""):
    if not (x <= y):
        raise Exception("%s NOT (%r <= %r)" % (msg, x, y))

def check_lt(x, y, msg=""):
    if not (x < y):
        raise Exception("%s NOT (%r < %r)" % (msg, x, y))

def check_gt(x, y, msg=""):
    if not (x > y):
        raise Exception("%s NOT (%r > %r)" % (msg, x, y))

def check_ge(x, y, msg=""):
    if not (x >= y):
        raise Exception("%s NOT (%r >= %r)" % (msg, x, y))

def check_in(x, y, msg=""):
    if not (x in y):
        raise Exception("%s NOT (%r in %r)" % (msg, x, y))

def check_not_in(x, y, msg=""):
    if not (x not in y):
        raise Exception("%s NOT (%r not in %r)" % (msg, x, y))

def _itertools_product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)

def setup_itertools_product():
    if platform.python_version().startswith("2.5"):
        itertools.product = _itertools_product

fp_pattern = r"(?:(?:[0-9]+\.?[0-9]*)|(?:\.[0-9]+))(?:[eE][+-]?[0-9]+)?"
i_pattern = r"(?:[+-]?[0-9]+)"
bool_pattern = r"(?:False)|(?:True)"

def abstract():
    import inspect
    caller = inspect.getouterframes(inspect.currentframe())[1][3]
    raise NotImplementedError(caller + ' must be implemented in subclass')

def load_or_build_and_save_repr(builder, filename, global_dict, local_dict):
    if os.path.isfile(filename):
        with open(filename, "rt") as in_file:
            obj = eval(in_file.read(), global_dict, local_dict)
    else:
        obj = builder()
        with open(filename, "wt") as out_file:
            out_file.write(repr(obj))
    return obj

def logsumexp(a, dtype=numpy.longfloat):
    """Compute the log of the sum of exponentials log(e^{a_1}+...e^{a_n})
    of the components of the array a, avoiding numerical overflow.
    See numpy.logaddexp
    """
    a = numpy.asarray(a) if not dtype else numpy.asarray(a, dtype)
    check_False(numpy.isnan(a).any(), a)
    a_max = a.max()
    if a_max == -numpy.inf:
        return a_max
    elif a_max == numpy.inf:
        return numpy.inf
    value = a_max + numpy.log((numpy.exp(a - a_max)).sum())
    check_False(numpy.isnan(value), a)
    return value

def world_time_string(t=None):
    """
    Params:
        t: obtained by calling time.time()
    """
    return time.strftime("%Y%m%d_%H%M%S", time.gmtime(t))
